#include "types.h"
#include "globals.h"
#include "kernel.h"
#include "errno.h"

#include "util/debug.h"

#include "proc/proc.h"

#include "mm/mm.h"
#include "mm/mman.h"
#include "mm/page.h"
#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/pagetable.h"

#include "vm/pagefault.h"
#include "vm/vmmap.h"

/*
 * This gets called by _pt_fault_handler in mm/pagetable.c The
 * calling function has already done a lot of error checking for
 * us. In particular it has checked that we are not page faulting
 * while in kernel mode. Make sure you understand why an
 * unexpected page fault in kernel mode is bad in Weenix. You
 * should probably read the _pt_fault_handler function to get a
 * sense of what it is doing.
 *
 * Before you can do anything you need to find the vmarea that
 * contains the address that was faulted on. Make sure to check
 * the permissions on the area to see if the process has
 * permission to do [cause]. If either of these checks does not
 * pass kill the offending process, setting its exit status to
 * EFAULT (normally we would send the SIGSEGV signal, however
 * Weenix does not support signals).
 *
 * Now it is time to find the correct page (don't forget
 * about shadow objects, especially copy-on-write magic!). Make
 * sure that if the user writes to the page it will be handled
 * correctly.
 *
 * Finally call pt_map to have the new mapping placed into the
 * appropriate page table.
 *
 * @param vaddr the address that was accessed to cause the fault
 *
 * @param cause this is the type of operation on the memory
 *              address which caused the fault, possible values
 *              can be found in pagefault.h
 */
void handle_pagefault(uintptr_t vaddr, uint32_t cause) {
	dbg(DBG_ELF,"entered handle_pagefault with vaddr %d\n",vaddr);
	uint32_t pn=ADDR_TO_PN(vaddr);
	dbg(DBG_VM,"vaddr:%u, pn:%u that cause page fault\n",vaddr,pn);
	vmarea_t *vmarea_f;
	vmarea_f = vmmap_lookup(curproc->p_vmmap, ADDR_TO_PN(vaddr));
	if(vmarea_f==NULL)
	{
	
		curproc->p_status = EFAULT;
		dbg(DBG_VM,"%d vmmap not found. The process would be shut down quietly.\n",ADDR_TO_PN(vaddr));
        	proc_kill(curproc,EFAULT);
       return;
	}
	if ((cause & FAULT_WRITE) && (vmarea_f->vma_prot & PROT_NONE)) {
		curproc->p_status = EFAULT;
		dbg(DBG_ELF,"Do not find page in handle_pagefault (Fisrt condition)\n");
		return;
	}
	int retv = 0;
	uintptr_t paddr;
	uint32_t pdflags = 0, ptflags = 0;

	

	pframe_t *resultf = NULL;
	dbg(DBG_ELF,"Finding pageframe %d, flag is %u\n",ADDR_TO_PN(vaddr)-vmarea_f->vma_start+vmarea_f->vma_off,vmarea_f->vma_flags);
	if((vmarea_f->vma_flags&MAP_PRIVATE)!=0)
	{
		dbg(DBG_ELF,"Object is shadowed, write permission is %d\n",cause & FAULT_WRITE);
		retv = pframe_lookup(vmarea_f->vma_obj, ADDR_TO_PN(vaddr)-vmarea_f->vma_start+vmarea_f->vma_off,cause & FAULT_WRITE,&resultf);
		if(retv==-1)
			retv = pframe_get(vmarea_f->vma_obj->mmo_un.mmo_bottom_obj, ADDR_TO_PN(vaddr)-vmarea_f->vma_start+vmarea_f->vma_off,&resultf);
	}	
	else
	retv = pframe_get(vmarea_f->vma_obj, ADDR_TO_PN(vaddr)-vmarea_f->vma_start+vmarea_f->vma_off,&resultf);
	if (retv < 0) {
		dbg(DBG_ELF,"Do not find page in handle_pagefault\n");
		curproc->p_status = EFAULT;
		return;
	}
	dbg(DBG_ELF,"Find page %d in handle_pagefault\n",ADDR_TO_PN(vaddr));
	paddr = pt_virt_to_phys((uint32_t) resultf->pf_addr);

	if (cause & FAULT_WRITE) {
		pdflags = pdflags | PD_WRITE;
		ptflags = ptflags | PT_WRITE;
	}
	if (cause & FAULT_USER) {
		pdflags = pdflags | PD_USER;
		ptflags = ptflags | PT_USER;
	}
	pdflags = pdflags | PD_PRESENT;
	ptflags = ptflags | PT_PRESENT;
	pt_map(curproc->p_pagedir,(uintptr_t)PAGE_ALIGN_DOWN(vaddr),(uintptr_t)PAGE_ALIGN_DOWN(paddr), pdflags, ptflags);
	dbg(DBG_ELF,"LEAVE handle_pagefault\n");
	/*        NOT_YET_IMPLEMENTED("VM: handle_pagefault");*/
}
